﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using VersionComparer.Base;
using VersionComparer.Comparer;
using VersionComparer.DataModel.Assemblies.Images;

namespace VersionComparer.DataModel.Assemblies
{
    [Serializable]
    public abstract class AssemblyItemBase <T>: ComparableItem
    {
        #region fields
        string underlyingTypeName;
        RtfBuilder typeRtf = new RtfBuilder ();
        string name;
        string text;
        string accessorsAsCode;
        ComparableItem[] childItems;
        AttributeItem[] attributes;
        #endregion fields

        #region constructors
        public AssemblyItemBase()
        {
        }
        public AssemblyItemBase(ComparableItem parent, T value): base(parent)
        {
            
            
        }
        #endregion constructors

        #region statics
        protected static string AddSpaceIfNotEmpty(string value)
        {
            if (String.IsNullOrEmpty(value))
                return "";
            else
                return value + " ";
        }

        protected static void BuildValueRtf(Type type, object value, RtfBuilder builder)
        {
            
            if (value == null)
            {
                builder.Append("null", keywordColor);
                return;
            }

            if (type == typeof(string))
            {
                builder.Append(string.Format("\"{0}\"", value), stringColor);
                return;
            }
            if (type.IsEnum)
            {
                BuildTypeRtf(builder, type, true);

                builder.Append(string.Format(".{0}", Enum.ToObject(type, value)));
                return;
            }
            string valueAsString = value.ToString();
            if (type == typeof(float))
                valueAsString = valueAsString.Replace(",", ".") + "F";

            builder.Append(valueAsString);
        }
        protected static void BuildTypeRtf(RtfBuilder rtf, Type type, bool fullNamespace)
        {
            
            if (type.IsValueType && !type.IsEnum)
            {
                if (type == typeof(int))
                {
                    rtf.Append("int", keywordColor);
                    return;
                }
                if (type == typeof(void))
                {
                    rtf.Append("void", keywordColor);
                    return;
                }

                if (type == typeof(object))
                {
                    rtf.Append("object", keywordColor);
                    return;
                }


                if (type == typeof(long))
                {
                    rtf.Append("long", keywordColor);
                    return;
                }
                if (type == typeof(byte))
                {
                    rtf.Append("byte", keywordColor);
                    return;
                }
                if (type == typeof(char))
                {
                    rtf.Append("char", keywordColor);
                    return;
                }
                if (type == typeof(float))
                {
                    rtf.Append("float", keywordColor);
                    return;
                }

                if (type == typeof(double))
                {
                    rtf.Append("double", keywordColor);
                    return;
                }

            }
            else
            {
                if (type == typeof(string))
                {
                    rtf.Append("string", keywordColor);
                    return;
                }
            }
            BuildTypeNameRtf(rtf, type, fullNamespace);
            if (type.IsGenericParameter)
            {
                rtf.Append(" <");
                var arguments = type.GetGenericArguments();
                for (int i = 0; i < arguments.Length; i++)
                {
                    var genericArgument = arguments[i];
                    if (i > 0)
                        rtf.Append(" , ");
                    BuildTypeNameRtf(rtf, genericArgument, fullNamespace);
                }
                rtf.Append("> ");
            }
        }

        private static void BuildTypeNameRtf(RtfBuilder rtf, Type type, bool fullNamespace)
        {
            if (fullNamespace)
            {
                if (type.IsNested)
                {
                }
                rtf.Append(type.Namespace + ".");
            }
            rtf.Append(type.Name, nameColor);
        }



        public static Color commentColor = Color.Green;
        public static Color nameColor = Color.FromArgb(43, 145, 175);
        public static Color keywordColor = Color.Blue;
        public string UnderlyingTypeName
        {
            get
            {
                return underlyingTypeName;
            }
        }
        public static Color stringColor = Color.FromArgb(163, 21, 21);
        public static ComparableItem CreateType(Type type, ComparableItem parent, string shallowName = null)
        {
            if (type.IsEnum)
                return Create<EnumItem, Type>(type, parent, shallowName);
            if (type.IsInterface)
                return Create<InterfaceItem, Type>(type, parent, shallowName);
            if (type.IsValueType)
                return Create<StructItem, Type>(type, parent, shallowName);
            if (type.BaseType == typeof(System.MulticastDelegate))
                return Create<DelegateItem, MethodInfo>(type.GetMethod("Invoke"), parent, type.Name);

            return Create<TypeItem, Type>(type, parent, shallowName);
        }
        public static T1 Create<T1, T2>(T2 value, ComparableItem parent, string shallowName = null)
            where T1 : AssemblyItemBase<T2>, new()
        {
           
            T1 newItem = new T1();
            try
            {
                newItem.Parent = parent;
                newItem.ShallowName = shallowName;
                newItem.InitializeAssemblyItem(value);
            }
            catch
            {
            }
            return newItem;
        }

        #endregion statics

        #region type related
        protected RtfBuilder UnderlyingTypeRtf
        {
            get
            {
                return typeRtf;
            }
        }
        #endregion type related

        #region attributes related
        protected void BuildAttributesRtf(RtfBuilder builder,bool differencesOnly , bool useParentColor , bool addEndOfLine = true)
        {
            AttributeItem[] childs = attributes;
            if (differencesOnly && SubItems != null)
            {
                childs = SubItems.OfType<AttributeItem>().ToArray();
            }
            foreach (var attribute in childs)
            {
                attribute.GetAsRtf(builder, differencesOnly, useParentColor);
            }
        }
        protected virtual CustomAttributeData [] GetAttributes ( T value )
        {
            return new CustomAttributeData[0];
        }
        protected virtual AttributeItem[] Attributes(T value)
        {
            var attributeDatas = GetAttributes(value);
            List<AttributeItem> result = new List<AttributeItem> ();
            foreach (var attribute in attributeDatas)
                result.Add(Create<AttributeItem, CustomAttributeData>(attribute, this));
            return result.ToArray();
        }
        protected virtual string GetAttributesCode()
        {
            if (attributes == null || attributes.Length == 0)
                return "";
            StringBuilder result = new StringBuilder();
            foreach (var att in attributes)
                result.AppendFormat("{0}\r\n", att.FullText);
            return result.ToString();
        }
        #endregion attributes related

        #region properties
        protected virtual bool ShowPrivate
        {
            get
            {
                return true;
            }
        }
        protected virtual string AccessorsAsCode
        {
            get
            {
                if (accessorsAsCode == null)
                {
                    string accessor = AddSpaceIfNotEmpty(GetAccessibilityAsCode(IsPublic, IsAssembly, IsFamilyOrAssembly, IsFamily));
                    string isNew = AddSpaceIfNotEmpty(IsNew ? "new" : "");
                    string isVirtual = AddSpaceIfNotEmpty((IsVirtual && !IsOverride) ? "virtual" : "");
                    string isOverride = AddSpaceIfNotEmpty(IsOverride ? "override" : "");
                    string isStatic = AddSpaceIfNotEmpty(IsStatic ? "static" : "");
                    string isSealed = AddSpaceIfNotEmpty(IsStatic ? "sealed" : "");
                    string isAbstract = AddSpaceIfNotEmpty(IsAbstract ? "abstract" : "");

                    accessorsAsCode = string.Format("{0}{1}{2}{3}{4}{5}{6}", accessor, isNew, isVirtual, isOverride, isStatic, isAbstract, isSealed);
                }
                return accessorsAsCode;
            }
        }
        public virtual bool IsFamily
        {
            get;
            protected set;
        }
        public virtual bool IsSealed
        {
            get;
            protected set;
        }
        public virtual bool IsFamilyOrAssembly
        {
            get;
            protected set;

        }
        public virtual bool IsNew
        {
            get;
            protected set;
        }
        public virtual bool IsVirtual
        {
            get;
            protected set;
        }
        public virtual bool IsConst
        {
            get;
            set;
        }
        public virtual bool IsPublic
        {
            get;
            protected set;
        }
        public virtual bool IsStatic
        {
            get;
            protected set;
        }
        public virtual bool IsAssembly
        {
            get;
            protected set;
        }
        public virtual bool IsAbstract
        {
            get;
            protected set;
        }
        public virtual bool IsOverride
        {
            get;
            protected set;
        }
        #endregion properties

        #region init related
        protected T1 Create<T1, T2>(T2 value)
            where T1 : AssemblyItemBase<T2>, new()
        {
            return Create<T1, T2>(value, this);
            
        }


        protected virtual Type AssociatedType (T value)
        {
            return null;
        }
        protected virtual void Initialize(T value)
        {
            InitializeAssemblyItem(value);
        }
        private void InitializeType(Type underlyingType)
        {
            
            if (underlyingType != null)
            {
                RtfBuilder aux = new RtfBuilder();
                BuildTypeRtf(aux, underlyingType, false);
                underlyingTypeName = aux.Text;
                BuildTypeRtf(typeRtf, underlyingType, true);
            }
        }
        

        public virtual void InitializeAssemblyItem(T value)
        {
            List<ComparableItem> allChildItems = new List<ComparableItem>();
            allChildItems.AddRange(attributes =Attributes(value));
            allChildItems.AddRange(GetChilds(value));
            childItems = allChildItems.ToArray();
            Type assotiatedType = null;
            try
            {
                assotiatedType= AssociatedType(value);
            }
            catch
            {
            }
            InitializeType(assotiatedType);
            Initialize(value);
            FullText = GetFullText(value);
            
            Description = FullText;
            
        }
        protected virtual string GetFullText(T value)
        {
            if ( string.IsNullOrEmpty ( FullText ))
                return Text;
            return FullText;
        }
        protected override ComparableItem[] CreateChilds()
        {
            if (this is ConstructorItem && Name == "ImpersonationActivityLogEntry")
            {
            }
            if (childItems == null)
            {
                return new ComparableItem[0];
            }
            return childItems;
        }
        protected virtual ComparableItem[] GetChilds(T value)
        {

            return new ComparableItem[0];
        }

        #endregion init related
        
        protected static string Indent(string text, int indentLevel)
        {
            if (text == null)
                return "";

            string indent = "";
            for ( int i = 0; i < indentLevel ; i++ )
                indent+="    ";
            return indent + text.Replace("\r\n", "\r\n" + indent);
        }
        protected override int CompareToItem(ComparableItem other)
        {
            return Name.CompareTo((other as AssemblyItemBase<T>).Name);
        }
        public override double Matches(ComparisonAlgorithm algorithm, ComparableItem other)
        {
            return Name == (other as AssemblyItemBase<T>).Name ? Match.CertainMatch : Match.NoMatch;
        }
        protected override int HashCodeForMatch
        {
            get
            {
                return Name.GetHashCode();
            }
        }
        
        #region accessibility related
        protected string GetAccessibilityAsCode(bool isPublic, bool isAssembly , bool isFamilyOrAssembly, bool isFamily)
        {
            return GetAccessibilityAsCode ( GetAccessibility ( isPublic , isAssembly , isFamilyOrAssembly , isFamily));
        }
        protected string GetAccessibilityAsCode(Accessibility accessibility)
        {
            switch (accessibility)
            {
                case Accessibility.Public:
                case Accessibility.Internal:
                case Accessibility.Protected:
                    return accessibility.ToString().ToLower();
                case Accessibility.Private:
                    if (ShowPrivate)
                        return accessibility.ToString().ToLower();
                    else
                        return "";
                case Accessibility.ProtectedInternal:
                    return "protected internal";
                default:
                    return "unknown";
            }
        }
        protected static Accessibility GetAccessibility(bool isPublic, bool isAssembly , bool isFamilyOrAssembly, bool isFamily)
        {
            if (isPublic)
                return Accessibility.Public;
            if (isAssembly )
                return Accessibility.Internal;
            if (isFamily)
                return Accessibility.Protected;
            if (isFamilyOrAssembly)
                return Accessibility.ProtectedInternal;
            return Accessibility.Private;
        }

        #endregion accessibility related

        public override string Text
        {
            get
            {
                if (text == null)
                {
                    if (Shallow)
                        text = string.Format("{0}: {1}", ShallowName, Name);
                    text = Name;
                }
                return text;
            }
            set
            {
                text = value;
            }
        }
        protected virtual string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }
        protected string ShallowName
        {
            get;
            set;
        }
        protected bool Shallow
        {
            get;
            set;
        }
        public override bool AllowsCopying
        {
            get
            {
                return false;
            }
        }
        public override bool AllowsChangingValue
        {
            get
            {
                return false;
            }
        }

        #region image related
        static ImageVariants imageVariants = new ImageVariants();
        static AssemblyItemBase()
        {
            imageVariants.Initialize (ImageVariants.ImageVariantPosition.BottomLeft, AssemblyResources._private, AssemblyResources._protected, AssemblyResources._internal);
        }
        protected virtual Image GetSpecificImage()
        {
            return null;
        }
        protected override Image GetImage()
        {
            
            if (Difference == DataModel.Difference.Unexistant)
                return RelatedItem.Image;
            int variant = 0;
            var accessibility = GetAccessibility ( IsPublic, IsAssembly , IsFamilyOrAssembly, IsFamily);
            switch (accessibility)
            {
                case Accessibility.Private:
                    variant = 1;
                    break;
                case Accessibility.Protected:
                case Accessibility.ProtectedInternal:
                    variant = 2;
                    break;
                case Accessibility.Internal:
                    variant = 3;
                    break;
            }
            return imageVariants.GetImageVariant ( GetSpecificImage(),variant);
        }
        #endregion image related
    }
}
